﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Security.Cryptography.X509Certificates;

namespace Sunny.Lib
{
    public class EncryptAndDecryptHelper
    {
        private static Encoding defaultEncoding = Encoding.ASCII;

        public static string Encrypt(Algorithm algorithm, string inputStr, Encoding encoding = null, EncryptSetting setting = null)
        {
            string outPutString = string.Empty;

            switch (GetAlgorithmTypeByAlgorithm(algorithm))
            {
                case AlgorithmType.Hash:
                    outPutString = EncryptByHash(algorithm, inputStr, encoding);
                    break;
                case AlgorithmType.Symmetry:
                    outPutString = EncryptBySymmetryAlgorithm(algorithm, inputStr, encoding, setting as SymmetrySetting);
                    break;
                case AlgorithmType.Asymmetry:
                    outPutString = EncryptByAsymmetricAlgorithm(algorithm, inputStr, encoding, setting as AsymmetrySetting);
                    break;
                default:
                    break;
            }

            return outPutString;
        }

        public static string Decrypt(Algorithm algorithm, string inputStr, Encoding encoding = null, EncryptSetting setting = null)
        {
            string outPutString = string.Empty;

            switch (GetAlgorithmTypeByAlgorithm(algorithm))
            {
                case AlgorithmType.Symmetry:
                    outPutString = DecryptBySymmetryAlgorithm(algorithm, inputStr,encoding, setting as SymmetrySetting);
                    break;
                case AlgorithmType.Asymmetry:
                    outPutString = DecryptByASymmetryAlgorithm(algorithm, inputStr, encoding, setting as AsymmetrySetting);
                    break;
                default:
                    //
                    break;
            }

            return outPutString;
        }

        public static string EncryptByHash(Algorithm algorithm, string sourceString, Encoding encoding = null)
        {
            string encryptedString = string.Empty;

            byte[] encryptedBytes = HashAlgorithm.EncryptByHash(algorithm, sourceString, (encoding == null ? defaultEncoding : encoding));
            encryptedString = Bytes2HexadecimalString(encryptedBytes);

            return encryptedString;
        }

        public static string EncryptBySymmetryAlgorithm(Algorithm algorithm, string sourceString, Encoding encoding = null, SymmetrySetting setting = null)
        {
            string encryptedString = string.Empty;

            encryptedString = SymmetryAlgorithm.Encrypt(algorithm, sourceString, (encoding == null ? defaultEncoding : encoding), setting);

            return encryptedString;
        }

        public static string EncryptByAsymmetricAlgorithm(Algorithm algorithm, string sourceString, Encoding encoding = null, AsymmetrySetting setting = null)
        {
            string encryptedString = string.Empty;

            encryptedString = AsymmetricAlgorithm.Encrypt(algorithm, sourceString, (encoding == null ? defaultEncoding : encoding), setting);

            return encryptedString;
        }

        public static string DecryptBySymmetryAlgorithm(Algorithm algorithm, string encryptedString, Encoding encoding = null, SymmetrySetting setting = null)
        {
            string sourceString = string.Empty;

            sourceString = SymmetryAlgorithm.Decrypt(algorithm, encryptedString, (encoding == null ? defaultEncoding : encoding), setting);

            return sourceString;
        }

        public static string DecryptByASymmetryAlgorithm(Algorithm algorithm, string encryptedString, Encoding encoding = null, AsymmetrySetting setting = null)
        {
            string sourceString = string.Empty;

            sourceString = AsymmetricAlgorithm.Decrypt(algorithm, encryptedString, (encoding == null ? defaultEncoding : encoding), setting);

            return sourceString;
        }

        public static string SignatureByAsymmetricAlgorithm(Algorithm algorithm, string sourceString, Encoding encoding = null, AsymmetrySetting setting = null)
        {
            string signedString = string.Empty;

            signedString = AsymmetricAlgorithm.Signature(algorithm, sourceString, (encoding == null ? defaultEncoding : encoding), setting);

            return signedString;
        }

        public static bool VerifySignatureByByAsymmetricAlgorithm(Algorithm algorithm, string sourceString, string signedString, Encoding encoding = null, AsymmetrySetting setting = null)
        {
            bool result = true;

            result = AsymmetricAlgorithm.VerifySignature(algorithm, sourceString, signedString, (encoding == null ? defaultEncoding : encoding), setting);

            return result;
        }

        public static AlgorithmType GetAlgorithmTypeByAlgorithm(Algorithm algorithm)
        {
            AlgorithmType algorithmType = AlgorithmType.Hash;

            switch (algorithm)
            {
                case Algorithm.MD5:
                case Algorithm.SHA1:
                case Algorithm.SHA256:
                case Algorithm.SHA384:
                case Algorithm.SHA512:
                    algorithmType = AlgorithmType.Hash;
                    break;
                case Algorithm.DES:
                case Algorithm.AES:
                case Algorithm.Rijndael:
                case Algorithm.TripleDES:
                    algorithmType = AlgorithmType.Symmetry;
                    break;
                case Algorithm.RSA:
                case Algorithm.DSA:
                case Algorithm.ECDSA:
                    algorithmType = AlgorithmType.Asymmetry;
                    break;
                default:
                    new Exception(string.Format("Not supported this algorithm type:{0}", algorithm.ToString()));
                    break;
            }

            return algorithmType;
        }

        public static Algorithm GetAlgorithmByName(string algorithmName)
        {
            Algorithm alg = Algorithm.MD5;

            Enum.TryParse<Algorithm>(algorithmName,out alg);

            return alg;
        }

        // "abc" -->16进制的字符串，如"61 62 63"
        public static string String2Binary(string sourceString, Encoding encoding)
        {
            string binary = string.Empty;

            byte[] bs = encoding.GetBytes(sourceString);
            binary = Bytes2HexadecimalString(bs);

            return binary;
        }

        // 将16进制的字符串，如"61 62 63"转化成原字符"abc"
        public static string Binary2String(string binaryString, Encoding encoding)
        {
            string sourceString = string.Empty;

            byte[] bs = HexadecimalString2Bytes(binaryString);
            sourceString = encoding.GetString(bs);

            return sourceString;
        }

        // 将Byte数组转换成十六进制字符串
        public static string Bytes2HexadecimalString(byte[] bs)
        {
            string hexString = string.Empty;

            if (bs.Count() > 0)
            {
                StringBuilder sb = new StringBuilder();
                foreach (byte b in bs)
                {
                    sb.AppendFormat("{0:X2} ", b);
                }
                hexString = sb.ToString().TrimEnd();
            }

            return hexString;
        }

        // 将十六进制字符串转换成Byte数组
        public static byte[] HexadecimalString2Bytes(string hexadecimalString)
        {
            byte[] bs = null;

            char[] splitChars = new char[] { ' ' };
            if (!string.IsNullOrEmpty(hexadecimalString))
            {
                string[] hexs = hexadecimalString.Split(splitChars);
                bs = new byte[hexs.Count()];
                int i = 0;
                foreach (string hex in hexs)
                {
                    int iv = Convert.ToInt32(hex, 16);
                    bs[i++] = Convert.ToByte(iv);
                }
            }

            return bs;
        }
    }

    // Hash algorthm
    public class HashAlgorithm
    {
        public static byte[] EncryptByHash(Algorithm algorithm, string sourceString, Encoding encoding)
        {
            byte[] encryptedBytes;

            byte[] sourceBytes = encoding.GetBytes(sourceString);
            switch (algorithm)
            {
                case Algorithm.MD5:
                    MD5 md5 = MD5.Create();
                    encryptedBytes = md5.ComputeHash(sourceBytes);
                    break;
                case Algorithm.SHA1:
                    SHA1 sha1 = SHA1.Create();
                    encryptedBytes = sha1.ComputeHash(sourceBytes);
                    break;
                case Algorithm.SHA256:
                    SHA1 sha256 = SHA1.Create();
                    encryptedBytes = sha256.ComputeHash(sourceBytes);
                    break;
                case Algorithm.SHA384:
                    SHA1 sha384 = SHA1.Create();
                    encryptedBytes = sha384.ComputeHash(sourceBytes);
                    break;
                case Algorithm.SHA512:
                    SHA1 sha512 = SHA1.Create();
                    encryptedBytes = sha512.ComputeHash(sourceBytes);
                    break;
                default:
                    new Exception(string.Format("Not supported this Hash type:{0}.", algorithm.ToString()));
                    encryptedBytes = null;
                    break;
            }

            return encryptedBytes;
        }
    }

    // Symmetry algorithm
    public class SymmetryAlgorithm
    {
        private static string key8;
        private static string Key8 // 8 Byte
        {
            get
            {
                if (string.IsNullOrEmpty(key8))
                {
                    key8 = DllHelper.SunnyDllHelper.GetSecurityString(DllHelper.SecurityStringType.EncryptKey8);
                }
                return key8;
            }
        }

        private static string key16;
        private static string Key16 // 16 Byte
        {
            get
            {
                if (string.IsNullOrEmpty(key16))
                {
                    key16 = DllHelper.SunnyDllHelper.GetSecurityString(DllHelper.SecurityStringType.EncryptKey16);
                }
                return key16;
            }
        }

        private static string key24;
        private static string Key24 // 24 Byte
        {
            get
            {
                if (string.IsNullOrEmpty(key24))
                {
                    key24 = DllHelper.SunnyDllHelper.GetSecurityString(DllHelper.SecurityStringType.EncryptKey24);
                }
                return key24;
            }
        }

        private static string key32;
        private static string Key32 // 32 Byte
        {
            get
            {
                if (string.IsNullOrEmpty(key32))
                {
                    key32 = DllHelper.SunnyDllHelper.GetSecurityString(DllHelper.SecurityStringType.EncryptKey32);
                }
                return key32;
            }
        }

        private static string iv8;
        private static string Iv8  // 8 Byte
        {
            get
            {
                if (string.IsNullOrEmpty(iv8))
                {
                    iv8 = DllHelper.SunnyDllHelper.GetSecurityString(DllHelper.SecurityStringType.EncryptIv8);
                }
                return iv8;
            }
        }

        private static string iv16;
        private static string Iv16  // 16 Byte
        {
            get
            {
                if (string.IsNullOrEmpty(iv16))
                {
                    iv16 = DllHelper.SunnyDllHelper.GetSecurityString(DllHelper.SecurityStringType.EncryptIv16);
                }
                return iv16;
            }
        }

        public static string Encrypt(Algorithm algorithm, string sourceString, Encoding encoding, SymmetrySetting setting = null)
        {
            string encryptString = string.Empty;

            Encoding codeType = encoding;
            string key = string.Empty;
            string iv = string.Empty;
            if (setting != null)
            {
                key = setting.Key;
                iv = setting.Iv;
            }

            switch (algorithm)
            {
                case Algorithm.DES:
                    MD5 md5 = MD5.Create();
                    encryptString = EncryptByDES(sourceString, codeType, key, iv);
                    break;
                case Algorithm.AES:
                    SHA1 sha1 = SHA1.Create();
                    encryptString = EncryptByAES(sourceString, codeType, key, iv);
                    break;
                case Algorithm.Rijndael:
                    SHA1 sha256 = SHA1.Create();
                    encryptString = EncryptByRijndae(sourceString, codeType, key, iv);
                    break;
                case Algorithm.TripleDES:
                    SHA1 sha384 = SHA1.Create();
                    encryptString = EncryptByTripleDES(sourceString, codeType, key, iv);
                    break;
                default:
                    new Exception(string.Format("Not supported this symmetry algorithm type:{0}.", algorithm.ToString()));
                    break;
            }

            return encryptString;
        }

        public static string Decrypt(Algorithm algorithm, string sourceString, Encoding encoding, SymmetrySetting setting = null)
        {
            string encryptString = string.Empty;

            Encoding codeType = encoding;
            string key = string.Empty;
            string iv = string.Empty;
            if (setting != null)
            {
                key = setting.Key;
                iv = setting.Iv;
            }

            switch (algorithm)
            {
                case Algorithm.DES:
                    MD5 md5 = MD5.Create();
                    encryptString = DecryptByDES(sourceString, codeType, key, iv);
                    break;
                case Algorithm.AES:
                    SHA1 sha1 = SHA1.Create();
                    encryptString = DecryptByAES(sourceString, codeType, key, iv);
                    break;
                case Algorithm.Rijndael:
                    SHA1 sha256 = SHA1.Create();
                    encryptString = DecryptByRijndae(sourceString, codeType, key, iv);
                    break;
                case Algorithm.TripleDES:
                    SHA1 sha384 = SHA1.Create();
                    encryptString = DecryptByTripleDES(sourceString, codeType, key, iv);
                    break;
                default:
                    new Exception("Not supported current Hash type.");
                    break;
            }

            return encryptString;
        }

        #region Symmetry encryption

        #region DES
        /// <summary>
        /// DES encryption algorithm
        /// </summary>
        /// <param name="sourceString">need to be encrypted</param>
        /// <param name="key">encrypt key, it's length must be 64 bits(byte[8])</param>
        /// <param name="iv">iv, it's length must be 64 bits(byte[8])</param>
        /// <returns></returns>
        public static string EncryptByDES(string sourceString, Encoding codeType, string key, string iv)
        {
            Encoding encoding = codeType;
            string Key = string.Empty;
            string Iv = string.Empty;
            if (string.IsNullOrEmpty(key))
            {
                Key = Key8;
            }
            else
            {
                Key = key;
            }
            if (string.IsNullOrEmpty(iv))
            {
                Iv = Iv8;
            }
            else
            {
                Iv = iv;
            }

            DESCryptoServiceProvider des = new DESCryptoServiceProvider();

            ICryptoTransform encryptor = des.CreateEncryptor(Encoding.ASCII.GetBytes(Key), Encoding.ASCII.GetBytes(Iv));
            using (MemoryStream ms = new MemoryStream())
            {
                byte[] inData = encoding.GetBytes(sourceString);
                using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                {
                    cs.Write(inData, 0, inData.Length);
                    cs.FlushFinalBlock();
                }
                return Convert.ToBase64String(ms.ToArray());
            }
        }

        /// <summary>
        /// DES decryption algorithm
        /// </summary>
        /// <param name="encryptedString">need to be decrypted</param>
        /// <param name="key">decrypt key, it's length must be 64 bits(byte[8])</param>
        /// <param name="iv">iv, it's length must be 64 bits(byte[8])</param>
        /// <returns></returns>
        public static string DecryptByDES(string encryptedString, Encoding codeType, string key, string iv)
        {
            Encoding encoding = codeType;
            string Key = string.Empty;
            string Iv = string.Empty;
            if (string.IsNullOrEmpty(key))
            {
                Key = Key8;
            }
            else
            {
                Key = key;
            }
            if (string.IsNullOrEmpty(iv))
            {
                Iv = Iv8;
            }
            else
            {
                Iv = iv;
            }

            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            ICryptoTransform decryptor = des.CreateDecryptor(Encoding.ASCII.GetBytes(Key), Encoding.ASCII.GetBytes(Iv));

            using (var ms = new MemoryStream())
            {
                Byte[] inData = Convert.FromBase64String(encryptedString);
                using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
                {
                    cs.Write(inData, 0, inData.Length);
                    cs.FlushFinalBlock();
                }
                return encoding.GetString(ms.ToArray());
            }
        }
        #endregion

        #region TripleDES（3DES）
        /// <summary>
        /// TripleDES encryption algorithm
        /// </summary>
        /// <param name="input">need to be encrypted</param>
        /// <param name="key">encrypt key, it's length must be 128 bits(byte[16])，192 bits(byte[24])</param>
        /// <param name="iv">iv, it's length must be 64 bits(byte[8])</param>
        /// <returns></returns>
        public static string EncryptByTripleDES(string sourceString, Encoding codeType, string key, string iv)
        {
            Encoding encoding = codeType;
            string Key = string.Empty;
            string Iv = string.Empty;
            if (string.IsNullOrEmpty(key))
            {
                Key = Key24;
            }
            else
            {
                Key = key;
            }
            if (string.IsNullOrEmpty(iv))
            {
                Iv = Iv8;
            }
            else
            {
                Iv = iv;
            }

            TripleDESCryptoServiceProvider des3 = new TripleDESCryptoServiceProvider();

            ICryptoTransform encryptor = des3.CreateEncryptor(Encoding.ASCII.GetBytes(Key), Encoding.ASCII.GetBytes(Iv));
            using (MemoryStream ms = new MemoryStream())
            {
                byte[] inData = encoding.GetBytes(sourceString);

                using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                {
                    cs.Write(inData, 0, inData.Length);
                    cs.FlushFinalBlock();
                }
                return Convert.ToBase64String(ms.ToArray());
            }
        }

        /// <summary>
        /// TripleDES decryption algorithm
        /// </summary>
        /// <param name="input">need to be decrypted</param>
        /// <param name="key">decrypt key, it's length must be 128 bits(byte[16])，192 bits(byte[24])</param>
        /// <param name="iv">iv, it's length must be 64 bits(byte[8])</param>
        /// <returns></returns>
        public static string DecryptByTripleDES(string encryptedString, Encoding codeType, string key, string iv)
        {
            Encoding encoding = codeType;
            string Key = string.Empty;
            string Iv = string.Empty;
            if (string.IsNullOrEmpty(key))
            {
                Key = Key24;
            }
            else
            {
                Key = key;
            }
            if (string.IsNullOrEmpty(iv))
            {
                Iv = Iv8;
            }
            else
            {
                Iv = iv;
            }

            TripleDESCryptoServiceProvider des3 = new TripleDESCryptoServiceProvider();

            ICryptoTransform decryptor = des3.CreateDecryptor(Encoding.ASCII.GetBytes(Key), Encoding.ASCII.GetBytes(Iv));
            using (var ms = new MemoryStream())
            {
                Byte[] inData = Convert.FromBase64String(encryptedString);

                using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
                {
                    cs.Write(inData, 0, inData.Length);
                    cs.FlushFinalBlock();
                }

                return encoding.GetString(ms.ToArray());
            }
        }
        #endregion

        #region AES
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sourceString">need to encrypt string</param>
        /// <param name="codeType">coding type</param>
        /// <param name="key">decrypt key, it's length must be 128 bits(byte[16])，192 bits(byte[24]),256 bits(byte[32])</param>
        /// <param name="iv">iv, it's length must be 128 bits(byte[16])</param>
        /// <returns>encrypted string</returns>
        public static string EncryptByAES(string sourceString, Encoding codeType, string key, string iv)
        {
            Encoding encoding = codeType;
            string Key = string.Empty;
            string Iv = string.Empty;
            if (string.IsNullOrEmpty(key))
            {
                Key = Key32;
            }
            else
            {
                Key = key;
            }
            if (string.IsNullOrEmpty(iv))
            {
                Iv = Iv16;
            }
            else
            {
                Iv = iv;
            }

            AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
            ICryptoTransform encryptor = aes.CreateEncryptor(Encoding.ASCII.GetBytes(Key), Encoding.ASCII.GetBytes(Iv));

            using (MemoryStream ms = new MemoryStream())
            {
                Byte[] inData = encoding.GetBytes(sourceString);

                using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                {
                    cs.Write(inData, 0, inData.Length);
                    cs.FlushFinalBlock();
                }

                return Convert.ToBase64String(ms.ToArray());
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="encryptedString">encrypted string</param>
        /// <param name="codeType">coding type</param>
        /// <param name="key">decrypt key, it's length must be 128 bits(byte[16])，192 bits(byte[24]),256 bits(byte[32])</param>
        /// <param name="iv">iv, it's length must be 128 bits(byte[16])</param>
        /// <returns>source string</returns>
        public static string DecryptByAES(string encryptedString, Encoding codeType, string key, string iv)
        {
            Encoding encoding = codeType;
            string Key = string.Empty;
            string Iv = string.Empty;
            if (string.IsNullOrEmpty(key))
            {
                Key = Key32;
            }
            else
            {
                Key = key;
            }
            if (string.IsNullOrEmpty(iv))
            {
                Iv = Iv16;
            }
            else
            {
                Iv = iv;
            }

            AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
            ICryptoTransform decryptor = aes.CreateDecryptor(Encoding.ASCII.GetBytes(Key), Encoding.ASCII.GetBytes(Iv));

            using (var ms = new MemoryStream())
            {
                Byte[] inData = Convert.FromBase64String(encryptedString);

                using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
                {
                    cs.Write(inData, 0, inData.Length);
                    cs.FlushFinalBlock();
                }

                return encoding.GetString(ms.ToArray());
            }
        }
        #endregion

        #region Rijndael
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sourceString">need to encrypt string</param>
        /// <param name="codeType">coding type</param>
        /// <param name="key">decrypt key, it's length must be 64 bits(byte[8])，128 bits(byte[16]),256 bits(byte[32])</param>
        /// <param name="iv">iv, it's length must be 128 bits(byte[16])</param>
        /// <returns>encrypted string</returns>
        public static string EncryptByRijndae(string sourceString, Encoding codeType, string key, string iv)
        {
            Encoding encoding = codeType;
            string Key = string.Empty;
            string Iv = string.Empty;
            if (string.IsNullOrEmpty(key))
            {
                Key = Key32;
            }
            else
            {
                Key = key;
            }
            if (string.IsNullOrEmpty(iv))
            {
                Iv = Iv16;
            }
            else
            {
                Iv = iv;
            }

            Rijndael rijndael = Rijndael.Create();
            ICryptoTransform encryptor = rijndael.CreateEncryptor(Encoding.ASCII.GetBytes(Key), Encoding.ASCII.GetBytes(Iv));

            using (MemoryStream ms = new MemoryStream())
            {
                Byte[] inData = encoding.GetBytes(sourceString);

                using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                {
                    cs.Write(inData, 0, inData.Length);
                    cs.FlushFinalBlock();
                }

                return Convert.ToBase64String(ms.ToArray());
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="encryptedString">encrypted string</param>
        /// <param name="codeType">coding type</param>
        /// <param name="key">decrypt key, it's length must be 64 bits(byte[8])，128 bits(byte[16]),256 bits(byte[32])</param>
        /// <param name="iv">iv, it's length must be 128 bits(byte[16])</param>
        /// <returns>source string</returns>
        public static string DecryptByRijndae(string encryptedString, Encoding codeType, string key, string iv)
        {
            Encoding encoding = codeType;
            string Key = string.Empty;
            string Iv = string.Empty;
            if (string.IsNullOrEmpty(key))
            {
                Key = Key32;
            }
            else
            {
                Key = key;
            }
            if (string.IsNullOrEmpty(iv))
            {
                Iv = Iv16;
            }
            else
            {
                Iv = iv;
            }

            AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
            ICryptoTransform decryptor = aes.CreateDecryptor(Encoding.ASCII.GetBytes(Key), Encoding.ASCII.GetBytes(Iv));

            using (var ms = new MemoryStream())
            {
                Byte[] inData = Convert.FromBase64String(encryptedString);

                using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
                {
                    cs.Write(inData, 0, inData.Length);
                    cs.FlushFinalBlock();
                }

                return encoding.GetString(ms.ToArray());
            }
        }
        #endregion

        #endregion
    }

    // private key used to sign, public key used to verify sign
    // public key used to encrypt, private key used to decrypt
    public class AsymmetricAlgorithm
    {
        public static string Encrypt(Algorithm algorithm, string sourceString, Encoding encoding = null, AsymmetrySetting setting = null)
        {
            string encryptedString = string.Empty;

            switch (algorithm)
            {
                case Algorithm.RSA:
                    if (setting.KeyType == KeyType.StringKey)
                    {
                        encryptedString = RSA_Encrypt(sourceString, encoding, setting.Key);
                    }
                    else
                    {
                        encryptedString = RSA_EncryptByCert(sourceString, encoding, CertificateHelper.GetCertificate(setting.Key, setting.Password));
                    }
                    break;
                case Algorithm.DSA:
                    break;
                case Algorithm.ECDSA:
                    break;
                default:
                    new Exception(string.Format("Not supported this asymmetry algorithm type:{0}.", algorithm.ToString()));
                    break;
            }

            return encryptedString;
        }

        public static string Decrypt(Algorithm algorithm, string encryptedString, Encoding encoding = null, AsymmetrySetting setting = null)
        {
            string sourceString = string.Empty;

            switch (algorithm)
            {
                case Algorithm.RSA:
                    if (setting.KeyType == KeyType.StringKey)
                    {
                        sourceString = RSA_Decrypt(encryptedString, encoding, setting.Key);
                    }
                    else
                    {
                        sourceString = RSA_DecryptByCert(encryptedString, encoding, CertificateHelper.GetCertificate(setting.Key, setting.Password));
                    }
                    break;
                case Algorithm.DSA:
                    break;
                case Algorithm.ECDSA:
                    break;
                default:
                    new Exception(string.Format("Not supported this asymmetry algorithm type:{0}.", algorithm.ToString()));
                    break;
            }

            return sourceString;
        }

        public static string Signature(Algorithm algorithm, string sourceString, Encoding encoding = null, AsymmetrySetting setting = null)
        {
            string signedString = string.Empty;

            switch (algorithm)
            {
                case Algorithm.RSA:
                    if (setting.KeyType == KeyType.StringKey)
                    {
                        signedString = Sha1RSA_Sign(sourceString, encoding, setting.Key);
                    }
                    else
                    {
                        signedString = Sha1RSA_SignByCert(sourceString, encoding, CertificateHelper.GetCertificate(setting.Key, setting.Password));
                    }
                    break;
                case Algorithm.DSA:
                    break;
                case Algorithm.ECDSA:
                    break;
                default:
                    new Exception(string.Format("Not supported this asymmetry algorithm type:{0}.", algorithm.ToString()));
                    break;
            }

            return signedString;
        }

        public static bool VerifySignature(Algorithm algorithm, string sourceString, string signedString, Encoding encoding, AsymmetrySetting setting = null)
        {
            bool result = true;

            switch (algorithm)
            {
                case Algorithm.RSA:
                    if (setting.KeyType == KeyType.StringKey)
                    {
                        result = Sha1RSA_VerifySign(sourceString, signedString, encoding, setting.Key);
                    }
                    else
                    {
                        result = Sha1RSA_VerifySignByCert(sourceString, signedString, encoding, CertificateHelper.GetCertificate(setting.Key, setting.Password));
                    }
                    break;
                case Algorithm.DSA:
                    break;
                case Algorithm.ECDSA:
                    break;
                default:
                    new Exception(string.Format("Not supported this asymmetry algorithm type:{0}.", algorithm.ToString()));
                    break;
            }

            return result;
        }

        #region RSA
        public static string RSA_Encrypt(string sourceString, Encoding encoding, string publicKey)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(publicKey);

            Byte[] cipherbytes = rsa.Encrypt(encoding.GetBytes(sourceString), false);
            return Convert.ToBase64String(cipherbytes);
        }

        public static string RSA_Decrypt(string encryptedString, Encoding encoding, string privateKey)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(privateKey);

            Byte[] cipherbytes = rsa.Decrypt(Convert.FromBase64String(encryptedString), false);
            return encoding.GetString(cipherbytes);
        }

        public static string Sha1RSA_Sign(string sourceString, Encoding encoding, string privateKey)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(privateKey);

            Byte[] cipherbytes = rsa.SignData(encoding.GetBytes(sourceString), "SHA1"); // hash at the first, then sign the result of hash

            return Convert.ToBase64String(cipherbytes);
        }

        public static bool Sha1RSA_VerifySign(string sourceString, string encryptedString, Encoding encoding, string publicKey)
        {
            bool result = true;

            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(publicKey);
            result = rsa.VerifyData(encoding.GetBytes(sourceString), "SHA1", Convert.FromBase64String(encryptedString));

            return result;
        }

        /// <summary>
        /// Sign message use certificate
        /// </summary>
        /// <param name="sourceString">message need to signature</param>
        /// <param name="encoding">incoding type</param>
        /// <param name="cert">certificate used to sign which must contains private key</param>
        /// <returns>encrypte string</returns>
        public static string Sha1RSA_SignByCert(string sourceString, Encoding encoding, X509Certificate2 cert)
        {
            string encryptedString = string.Empty;

            byte[] messagebytes = encoding.GetBytes(sourceString);
            SHA1 sha1 = new SHA1CryptoServiceProvider();
            byte[] hashbytes = sha1.ComputeHash(messagebytes);
            RSAPKCS1SignatureFormatter sign = new RSAPKCS1SignatureFormatter();
            sign.SetKey(cert.PrivateKey);   // Set private key
            sign.SetHashAlgorithm("SHA1");  // Set hash algorithm type
            byte[] result = sign.CreateSignature(hashbytes);
            encryptedString = Convert.ToBase64String(result);

            /*string privateKey = cert.PrivateKey.ToXmlString(false);
            encryptedString = Sha1RSA_Sign(sourceString, encoding, privateKey);*/

            return encryptedString;
        }

        /// <summary>
        /// Verify signature is right
        /// </summary>
        /// <param name="sourceString">message need to signature</param>
        /// <param name="encryptString">encrypted message</param>
        /// <param name="encoding">encoding type</param>
        /// <param name="cert">certificate used to verify signature which must contains public key</param>
        /// <returns></returns>
        public static bool Sha1RSA_VerifySignByCert(string sourceString, string encryptString, Encoding encoding, X509Certificate2 cert)
        {
            bool result = true;

            string publicKey = cert.PublicKey.Key.ToXmlString(false);
            result = Sha1RSA_VerifySign(sourceString, encryptString, encoding, publicKey);

            return result;
        }

        public static string RSA_EncryptByCert(string sourceString, Encoding encoding, X509Certificate2 cert)
        {
            string encryptString = string.Empty;

            string publicKey = cert.PublicKey.Key.ToXmlString(false);
            encryptString = RSA_Encrypt(sourceString, encoding, publicKey);

            return encryptString;
        }

        public static string RSA_DecryptByCert(string encryptedString, Encoding encoding, X509Certificate2 cert)
        {
            string sourceString = string.Empty;

            RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
            Byte[] cipherbytes = rsa.Decrypt(Convert.FromBase64String(encryptedString), false);
            sourceString = encoding.GetString(cipherbytes);

            return sourceString;
        }
        #endregion

        #region DSA Signature
        public static string EncryptByDSA(string sourceString, string publicKey)
        {
            DSACryptoServiceProvider dsa = new DSACryptoServiceProvider();
            SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();

            dsa.FromXmlString(publicKey);
            Byte[] result = dsa.CreateSignature(sha1.ComputeHash(Convert.FromBase64String(sourceString)));
            return Convert.ToBase64String(result);
        }

        public static bool DecryptByDSA(string encryptedString, string privateKey, string originalString)
        {
            DSACryptoServiceProvider dsa = new DSACryptoServiceProvider();
            SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();

            dsa.FromXmlString(privateKey);
            return dsa.VerifySignature(sha1.ComputeHash(Convert.FromBase64String(originalString)), Convert.FromBase64String(encryptedString));
        }
        #endregion

        #region ECDSA Signature
        public static string EncryptByECDSA(string sourceString, CngKey key)
        {
            ECDsaCng ecdsa = new ECDsaCng(key);
            SHA1 sha1 = SHA1.Create();
            byte[] result = ecdsa.SignHash(sha1.ComputeHash(Convert.FromBase64String(sourceString)));
            return Convert.ToBase64String(result);
        }

        public static bool DecryptByECDSA(string encryptedString, CngKey key, string sourceString)
        {
            ECDsaCng ecdsa = new ECDsaCng(key);
            SHA1 sha1 = SHA1.Create();

            return ecdsa.VerifyHash(sha1.ComputeHash(Convert.FromBase64String(sourceString)), Convert.FromBase64String(encryptedString));
        }
        #endregion
    }

    public enum AlgorithmType
    {
        Symmetry,
        Hash,
        Asymmetry,
    }

    public enum Algorithm
    {
        DES = 1,            // Symmetry encryption algorithm
        AES = 2,            // Symmetry
        Rijndael = 3,       // Symmetry
        TripleDES = 4,      // Symmetry(3DES)
        MD5 = 5,            // Hash encryption algorithm
        SHA1 = 6,           // Hash
        SHA256 = 7,         // Hash
        SHA384 = 8,         // Hash
        SHA512 = 9,         // Hash
        RSA,                // Asymmetry encryption algorithm
        DSA,                // Asymmetry
        ECDSA               // Asymmetry
    }

    public enum KeyType
    {
        StringKey = 1,
        CertKey = 2,
    }

    public class AsymmetrySetting : EncryptSetting
    {
        // if KeyType = StringKey, Key is key
        // if KeyType =CertKey, Key is cert path
        public string Key { get; set; }

        // if KeyType =CertKey, Password is cert password
        // else nothing
        public string Password { get; set; }

        public KeyType KeyType { get; set; }
    }

    public class SymmetrySetting : EncryptSetting
    {
        public string Key { get; set; }
        public string Iv { get; set; }
    }

    public class EncryptSetting
    {
    }

    public class Helper
    {
        public const string a_z_0_9 = "abcdefghijklmnopqrstuvwsyzABCDEFGHIJKLMNOPQRSTUVWSYZ0123456789";

        private static char GenerateRandomString()
        {
            Random rd = new Random();
            int index = rd.Next(0, a_z_0_9.Length - 1);
            return a_z_0_9[index];
        }

        public static string InsertIntervalChar(string inString, int startIndex, int interval)
        {
            string outString;
            char[] temp = new char[inString.Length + inString.Length / interval + 1];

            int tempdex = 0;
            int insertindex = startIndex;
            for (int len = 0; len < inString.Length; len++)
            {
                if (insertindex == len)
                {
                    temp[tempdex++] = GenerateRandomString();
                    insertindex += interval;
                }
                temp[tempdex++] = inString[len];
            }

            outString = new string(temp);
            return outString;
        }

        public static string RemoveIntervalChar(string inString, int startIndex, int interval)
        {
            string outString = string.Empty;

            return outString;
        }
    }

    /*
     string str = "12345678"; 

            //--------------------des------------------------
            string sourceString = string.Empty;
            string encryptedString = string.Empty;
            SymmetryEncryptAlgorithm.key = "12345678"; // 8 byte length
            SymmetryEncryptAlgorithm.iv = "12345678";  // 8 byte length
            encryptedString = SymmetryEncryptAlgorithm.EncryptByDES(str, Encoding.Default);
            sourceString = SymmetryEncryptAlgorithm.DecryptByDES(encryptedString, Encoding.Default);

            //--------------------3des------------------------
            sourceString = string.Empty;
            encryptedString = string.Empty;
            SymmetryEncryptAlgorithm.key = "12345678ABCDEFGH"; // 16/24 byte length
            SymmetryEncryptAlgorithm.iv = "12345678";          // 8 byte length
            encryptedString = SymmetryEncryptAlgorithm.EncryptByTripleDES(str, Encoding.Default);
            sourceString = SymmetryEncryptAlgorithm.DecryptTripleDES(encryptedString, Encoding.Default);

            //--------------------aes--------------------------
            sourceString = string.Empty;
            encryptedString = string.Empty;
            SymmetryEncryptAlgorithm.key = "12345678ABCDEFGH"; // 16/24/32 byte length
            SymmetryEncryptAlgorithm.iv = "12345678ABCDEFGH";  // 16 byte length
            encryptedString = SymmetryEncryptAlgorithm.EncryptByAES(str, Encoding.Default);
            sourceString = SymmetryEncryptAlgorithm.DecryptByAES(encryptedString, Encoding.Default);

            //--------------------Rijndael---------------------
            sourceString = string.Empty;
            encryptedString = string.Empty;
            SymmetryEncryptAlgorithm.key = "12345678ABCDEFGH"; // 8/16/32 byte length
            SymmetryEncryptAlgorithm.iv = "12345678ABCDEFGH";  // 16 byte length
            encryptedString = SymmetryEncryptAlgorithm.EncryptByRijndae(str, Encoding.Default);
            sourceString = SymmetryEncryptAlgorithm.DecryptByRijndae(encryptedString, Encoding.Default);

            //--------------------RSA--------------------------
            sourceString = string.Empty;
            encryptedString = string.Empty;
            RSA rsa = RSA.Create();
            encryptedString = AsymmetricEncryptAlgorithm.EncryptByRSA(str,Encoding.Default, rsa.ToXmlString(false));
            sourceString = AsymmetricEncryptAlgorithm.DecryptByRSA(encryptedString, Encoding.Default, rsa.ToXmlString(true));

            //--------------------DSA--------------------------
            sourceString = string.Empty;
            encryptedString = string.Empty;
            DSA dsa = DSA.Create();
            encryptedString = AsymmetricEncryptAlgorithm.EncryptByDSA(str, dsa.ToXmlString(true));
            bool flag = AsymmetricEncryptAlgorithm.DecryptByDSA(encryptedString, dsa.ToXmlString(true), str);

            //--------------------ECDsa--------------------------
            sourceString = string.Empty;
            encryptedString = string.Empty;
            CngKey key1 = CngKey.Create(CngAlgorithm.ECDsaP256);
            encryptedString = AsymmetricEncryptAlgorithm.EncryptByECDSA(str, key1);
            flag = AsymmetricEncryptAlgorithm.DecryptByECDSA(encryptedString, key1, str);
     */
}
